package dht.server;

import dht.AbstractTransactionHandler;
import dht.IMetaServerRouteTable;
import dht.entity.Message;
import dht.entity.MessageType;
import dht.entity.NodeInfo;
import dht.socket.SocketHandler;
import dht.socket.TcpConnection;
import dht.throwable.DhtException;

public class MetaServerTransactionHandlerImpl extends
		AbstractTransactionHandler {

	protected IMetaServerRouteTable routeTable;

	public MetaServerTransactionHandlerImpl(NodeInfo localInfo,
			NodeInfo remoteInfo, IMetaServerRouteTable routeTable,
			TcpConnection tcpConnection) {
		super(localInfo, remoteInfo, tcpConnection);
		this.routeTable = routeTable;
	}

	public MetaServerTransactionHandlerImpl(NodeInfo localInfo,
			SocketHandler socketHandler, IMetaServerRouteTable routeTable,
			TcpConnection tcpConnection) {
		super(localInfo, socketHandler, tcpConnection);
		this.routeTable = routeTable;
	}

	@Override
	protected void runInitiative(Message msg) {
		switch (msg.getMsgType()) {
		case MOV_REQ:
			doMovReq(msg);
			break;
		default:
			break;
		}
	}

	@Override
	protected void runPassive() {
		Message reqMsg = null;
		try {
			reqMsg = socketHandler.readMessage();
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}
		switch (reqMsg.getMsgType()) {
		case SERVER_TABLE_REQ:
			handleServerTableReq(reqMsg);
			break;
		case CLIENT_TABLE_REQ:
			handleClientTableReq(reqMsg);
			break;
		case JOIN_REQ:
			handleJoinReq(reqMsg);
			break;
		default:
			break;
		}
	}

	protected void doMovReq(Message msg) {
		SocketHandler socketHandler = null;
		try {
			socketHandler = tcpConnection
					.genTransactionSocketHandler(remoteInfo);
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}
		try {
			socketHandler.sendMessage(msg);
			Message respMsg = socketHandler.readMessage();
			assert (respMsg.getMsgType() == MessageType.MOV_RESP);
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}

		routeTable
				.tableUpdate(msg.getMoveFromServerInfo().getServerId(), msg
						.getMoveToServerInfo().getServerId(), msg
						.getMovementVnodeIds());
		routeTable.endLoadBalancing(msg.getMoveFromServerInfo(),
				msg.getMoveToServerInfo());

		Message newMsg = new Message(msg.getTransactionId(),
				MessageType.DATA_RELEASE_REQ, localInfo);
		newMsg.setDataServerRouteTable(routeTable.getDataServerRouteTable());
		// TODO add release info

		Message tableMsg = new Message(msg.getTransactionId(),
				MessageType.TABLE_PUSH_REQ, localInfo);
		tableMsg.setDataServerRouteTable(routeTable.getDataServerRouteTable());

		SocketHandler nextSocketHandler = null;
		try {
			nextSocketHandler = tcpConnection.genTransactionSocketHandler(msg
					.getMoveToServerInfo());
			socketHandler.sendMessage(newMsg);
			nextSocketHandler.sendMessage(tableMsg);
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}

		try {
			Message respMsg = socketHandler.readMessage();
			assert (respMsg.getMsgType() == MessageType.DATA_RELEASE_RESP);
			respMsg = nextSocketHandler.readMessage();
			assert (respMsg.getMsgType() == MessageType.TABLE_PUSH_RESP);
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}

		try {
			socketHandler.closeSocket();
			nextSocketHandler.closeSocket();
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}
	}

	protected void handleServerTableReq(Message msg) {
		Message respMsg = new Message(msg.getTransactionId(),
				MessageType.SERVER_TABLE_RESP, localInfo);
		respMsg.setDataServerRouteTable(routeTable.getDataServerRouteTable());
		try {
			socketHandler.sendMessage(respMsg);
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}
		try {
			socketHandler.closeSocket();
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}
	}

	protected void handleClientTableReq(Message msg) {
		Message respMsg = new Message(msg.getTransactionId(),
				MessageType.CLIENT_TABLE_RESP, localInfo);
		respMsg.setClientRouteTable(routeTable.getClientRouteTable());
		try {
			socketHandler.sendMessage(respMsg);
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}
		try {
			socketHandler.closeSocket();
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}
	}

	protected void handleJoinReq(Message msg) {
		Message retMsg = new Message(msg.getTransactionId(),
				MessageType.JOIN_RESP, localInfo);
		retMsg.setDataServerRouteTable(routeTable.getDataServerRouteTable());
		try {
			socketHandler.sendMessage(retMsg);
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}
		try {
			Message respMsg = socketHandler.readMessage();
			assert (respMsg.getMsgType() == MessageType.JOIN_ACK);
			routeTable.join(msg.getSenderNodeInfo());
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}
		try {
			socketHandler.closeSocket();
		} catch (DhtException e) {
			e.printStackTrace();
			return;
		}
	}

}
